package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"sort"
	"strings"
)

const (
	COMPILED_SOURCE = "WashizuMahjong.js"
	SOURCE_DIR      = "source"
	COMPLETE_SOURCE = "completesource.txt"
)

// By is the type of a "less" function that defines the ordering of its FileInfo arguments.
type By func(p1, p2 os.FileInfo) bool

// Joins a By function and a slice of Planets to be sorted.
type fileSorter struct {
	files []os.FileInfo
	by    By
}

func main() {
	result := compile(SOURCE_DIR + "/" + COMPLETE_SOURCE)
	err := ioutil.WriteFile(COMPILED_SOURCE, []byte(result), 0644)
	if err != nil {
		fmt.Println("Compile failed!")
	}
	fmt.Println("Compile Successfull!")
}

// compile files from SOURCE_DIR to COMPILED_SOURCE
func compile(file string) string {
	//read the source
	cleanString := "/* Put the source code hear. */"
	_f, err := ioutil.ReadFile(file)
	if err != nil {
		return ""
	}

	r := completeSource(SOURCE_DIR, ".js")
	f := string(_f)

	return strings.Replace(f, cleanString, r, -1)
}

func completeSource(dir, filetype string) string {
	files, err := ioutil.ReadDir(dir)
	result := ""
	if err != nil {
		return ""
	}
	//sort files
	sortFunc := func(p1, p2 os.FileInfo) bool {
		keyPoint := "_"
		if strings.Index(p1.Name(), keyPoint) == 0 && strings.Index(p2.Name(), keyPoint) == 0 {
			return p1.Name() < p2.Name()
		} else if strings.Index(p1.Name(), keyPoint) == 0 {
			return true
		} else if strings.Index(p2.Name(), keyPoint) == 0 {
			return false
		} else {
			return p1.Name() < p2.Name()
		}
	}
	By(sortFunc).Sort(files)
	//complete
	for _, file := range files {
		if strings.Index(file.Name(), filetype) < 0 {
			continue
		}
		_f, _ := ioutil.ReadFile(dir + "/" + file.Name())
		f := string(_f)
		result += strings.Replace(f, findDocComment(f), "", -1)
		fmt.Println("Read file \"" + file.Name() + "\".")
	}
	return result
}

// Read the file and get the JSDOC of @fileOverview
func findDocComment(file string) string {
	if strings.Index(file, "*/") < 0 {
		return ""
	}
	leftComment := strings.Index(file, "/**")
	rightComment := strings.Index(file, "*/") + 2

	f := file[leftComment:rightComment]

	if strings.Index(f, "@fileOverview") >= 0 {
		return f
	}
	if strings.LastIndex(file, "*/")+2 == rightComment {
		return ""
	}
	return findDocComment(file[rightComment : strings.LastIndex(file, "*/")+2])
}

// Sort is a method on the function type, By, that sorts the argument slice according to the function.
func (by By) Sort(files []os.FileInfo) {
	fs := &fileSorter{
		files: files,
		by:    by, // The Sort method's receiver is the function (closure) that defines the sort order.
	}
	sort.Sort(fs)
}

// Len is part of sort.Interface.
func (s *fileSorter) Len() int {
	return len(s.files)
}

// Swap is part of sort.Interface.
func (s *fileSorter) Swap(i, j int) {
	s.files[i], s.files[j] = s.files[j], s.files[i]
}

// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s *fileSorter) Less(i, j int) bool {
	return s.by(s.files[i], s.files[j])
}
